#### TypeScript 编译
好的 IDE 支持对 TypeScript 的即时编译。但是，如果你想在使用 tsconfig.json 时从命令行手动运行 TypeScript 编译器，你可以通过以下方式：

运行 tsc，它会在当前目录或者是父级目录寻找 tsconfig.json 文件。
运行 tsc -p ./path-to-project-directory 。当然，这个路径可以是绝对路径，也可以是相对于当前目录的相对路径。
你甚至可以使用 tsc -w 来启用 TypeScript 编译器的观测模式，在检测到文件改动之后，它将重新编译。

#### 声明空间
class Foo {}
interface Bar {}
type Bas = {};

let foo: Foo;
let bar: Bar;
let bas: Bas;


#### 模块
###### 全局模块
###### 文件模块

AMD：不要使用它，它仅能在浏览器工作；
SystemJS：这是一个好的实验，已经被 ES 模块替代；
ES 模块：它并没有准备好。

##### 路径
相对模块路径（路径以 . 开头，例如：./someFile 或者 ../../someFolder/someFile 等）；
其他动态查找模块（如：core-js，typestyle，react 或者甚至是 react/core 等）。

./ ../直到系统的根目录

#### 命名空间

```js
(function(something) {
  something.foo = 123;
})(something || (something = {}));
```

namespace Utility 




#### TypeScript 类型系统

###### 基本注解
:TypeAnnotation
let age: number = 18;

###### 原始数据
string 、 number 、 boolean

###### 数组

```ts
const boolArray: boolean[];
```

###### 接口
接口是 TypeScript 的一个核心知识，它能合并众多类型声明至一个类型声明

```ts
interface Name {
  first: string;
  second: string;
}
```

###### 内联类型注解

```ts
let name: {
  first: string;
  second: string;
};
```

###### 特殊类型
any 减少依赖 null 、 undefined、void 

###### 泛型
```ts
function reverse<T>(items: T[]): T[] {
  const toreturn = [];
  for (let i = items.length - 1; i >= 0; i--) {
    toreturn.push(items[i]);
  }
  return toreturn;
}
```

###### 联合类型
string | number

###### 交叉类型
```ts
const x = extend({ a: 'hello' }, { b: 42 });
//现在x拥有了a属性与b属性 const a = x.a;
const b = x.b;
```

###### 元组类型
```ts
let nameNumber: [string, number]; 
nameNumber = ['Jenny', 221345];
```


### 从 JavaScript 迁移
    js => ts 
    any 使用
    d.ts
    
### @types
全局 模块 @types 直接使用 $
通过配置 compilerOptions.types: [ "jquery" ] 后，只允许使用 jquery 的 @types 包


### 环境声明
如果一个文件有扩展名 .d.ts ，这意味着每个顶级的声明都必须以 declare 关键字作为前 缀。这有利于向作者说明，在这里 TypeScript 将不会把它编译成任何代码，同时他需要确保这些在 编译时存在。
```ts
declare let process: any;
```

### 接口

接口旨在声明 JavaScript 中可能存在的任意结构。

// Sample A
declare const myPoint: { x: number; y: number };

// Sample B
interface Point {
    x: number;
    y: number;
}
declare const myPoint: Point;








### 枚举
```ts
enum CardSuit {
  Clubs,
  Diamonds,
  Hearts,
  Spades,
}
// 简单的使用枚举类型
let Card = CardSuit.Clubs;
Card = 'not a member of card suit';

var CardSuit;
(function (CardSuit) {
    CardSuit[CardSuit["Clubs"] = 0] = "Clubs";
    CardSuit[CardSuit["Diamonds"] = 1] = "Diamonds";
    CardSuit[CardSuit["Hearts"] = 2] = "Hearts";
    CardSuit[CardSuit["Spades"] = 3] = "Spades";
})(CardSuit || (CardSuit = {}));
// 简单的使用枚举类型
var Card = CardSuit.Clubs;
Card = 'not a member of card suit';

```
```ts
enum Color {
  DarkRed = 3, // 3
  DarkGreen, // 4
  DarkBlue // 5
}
```

```ts
export enum EvidenceTypeEnum {
    UNKNOWN = '',
    PASSPORT_VISA = 'passport_visa',
    PASSPORT = 'passport',
    SIGHTED_STUDENT_CARD = 'sighted_tertiary_edu_id',
    SIGHTED_KEYPASS_CARD = 'sighted_keypass_card',
    SIGHTED_PROOF_OF_AGE_CARD = 'sighted_proof_of_age_card'
}
```

### lib.d.ts
lib.d.ts 的内容主要是一些变量声明（如：window、document、math）和一些类似的接口声明（如：Window、Document、Math）。
```ts
interface Math {
  seedrandom(seed?: string): void;
}
```


### 函数
###### 参数注解

```ts
// variable annotation
let sampleVariable: { bar: number };

// function parameter annotation
function foo(sampleParameter: { bar: number }) {}
```

###### 返回类型注解

```ts
// Return type annotated as `: Foo`
function foo(sample: Foo): Foo {
  return sample;
}
```

###### 可选参数
```ts
function foo(bar: number, bas?: string): void {
  // ..
}

foo(123);
foo(123, 'hello');

function foo(bar: number, bas: string = 'hello') {
  console.log(bar, bas);
}

foo(123); // 123, hello
foo(123, 'world'); // 123, world
```

###### 重载
```ts
// 重载
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
// Actual implementation that is a true representation of all the cases the function body needs to handle
function padding(a: number, b?: number, c?: number, d?: number) {
  if (b === undefined && c === undefined && d === undefined) {
    b = c = d = a;
  } else if (c === undefined && d === undefined) {
    c = a;
    d = b;
  }
  return {
    top: a,
    right: b,
    bottom: c,
    left: d
  };
}
```

###### 函数声明

```ts
interface ReturnString {
  (): string;
}
```

```ts
type LongHand = {
  (a: number): number;
};

type ShortHand = (a: number) => number;
```

### 可调用的
```ts
interface ReturnString {
  (): string;
}
declare const foo: ReturnString;
const bar = foo(); // bar 被推断为一个字符串。
```


```ts
interface Overloaded {
  (foo: string): string;
  (foo: number): number;
}

// 实现接口的一个例子：
function stringOrNumber(foo: number): number;
function stringOrNumber(foo: string): string;
function stringOrNumber(foo: any): any {
  if (typeof foo === 'number') {
    return foo * foo;
  } else if (typeof foo === 'string') {
    return `hello ${foo}`;
  }
}

const overloaded: Overloaded = stringOrNumber;

// 使用
const str = overloaded(''); // str 被推断为 'string'
const num = overloaded(123); // num 被推断为 'number'
```

###### 箭头函数

const simple: (foo: number) => string = foo => foo.toString();

它仅仅只能作为简单的箭头函数，你无法使用重载。如果想使用重载，你必须使用完整的 { (someArgs): someReturn } 的语法

##### 可实例化


### 类型断言
TypeScript 允许你覆盖它的推断，并且能以你任何你想要的方式分析它，这种机制被称为「类型断言」

```ts
interface Foo {
  bar: number;
  bas: string;
}

const foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';
```

###### 双重断言
function handler(event: Event) {
  const mouseEvent = event as MouseEvent;
}
function handler(event: Event) {
  const element = (event as any) as HTMLElement; // ok
}

### Freshness

```ts
function logName(something: { name: string }) {
  console.log(something.name);
}

const person = { name: 'matt', job: 'being awesome' };
const animal = { name: 'cow', diet: 'vegan, but has milk of own specie' };
const randow = { note: `I don't have a name property` };

logName(person); // ok
logName(animal); // ok
logName(randow); // Error: 没有 `name` 属性

logName({ name: 'matt', job: 'being awesome' }) //变量不报错
```


```ts
// 假设
interface State {
  foo: string;
  bar: string;
}

function setState(s: State) {}

// 你可能想做：
setState({ foo: "Hello" }); // Error: 没有属性 'bar'

// 因为 state 包含 'foo' 与 'bar'，TypeScript 会强制你这么做：
setState({ foo: "Hello", bar: this.state.bar });

```


### 类型保护

##### typeof
typeof x === 'string'

##### instanceof
```ts
class Foo {
  foo = 123;
}

class Bar {
  bar = 123;
}

function doStuff(arg: Foo | Bar) {
  if (arg instanceof Foo) {
    console.log(arg.foo); // ok
    console.log(arg.bar); // Error
  } else {
    // 这个块中，一定是 'Bar'
    console.log(arg.foo); // Error
    console.log(arg.bar); // ok
  }
}
```

##### in
in 操作符可以安全的检查一个对象上是否存在一个属性，它通常也被作为类型保护使用

##### 字面量类型保护

当你在联合类型里使用字面量类型时，你可以检查它们是否有区别

##### 使用定义的类型保护

当你在使用普通的 JavaScript 对象时（使用结构类型，更有益处），你甚至无法访问 instanceof 和 typeof

### 字面量类型

##### 字符串字面量
let foo: 'Hello'; // 它仅接收一个字面量值为 Hello 的变量

##### 
其他字面量类型

```ts

type OneToFive = 1 | 2 | 3 | 4 | 5;
type Bools = true | false;

let a: OneToFive = 1;

let b: OneToFive = 9;
```

###### 推断

```ts

function iTakeFoo(foo: "foo") {}

const test = {
  someProp: "foo" as "foo",
};

iTakeFoo(test.someProp); // ok


function iTakeFoo(foo: "foo") {}
type Test = {
  someProp: "foo";
};
const test: Test = {
  // 推断 `someProp` 永远是 'foo' someProp: 'foo'
  someProp: "foo",
};
iTakeFoo(test.someProp); // ok

```

### readonly

```ts
type Foo = {
  readonly bar: number;
  readonly bas: number;
};

// 初始化
const foo: Foo = { bar: 123, bas: 456 };

// 不能被改变
// readonly :
foo.bar = 456; // Error: foo.bar 为仅读属性
```

```ts
class Foo {
  readonly bar = 1; // OK
  readonly baz: string;
  constructor() {
    this.baz = "hello"; // OK
  }
}

new Foo()

```


```ts
type Foo = {
  bar: number;
  bas: number;
};
type FooReadonly = Readonly<Foo>;

const foo: Foo = { bar: 123, bas: 456 };
const fooReadonly: FooReadonly = { bar: 123, bas: 456 };

foo.bar = 456; // ok
fooReadonly.bar = 456; // Error: bar 属性只读

```
把索引签名标记为只读
```ts
interface Foo {
  readonly [x: number]: number;
}

// 使用 6.
const foo: Foo = { 0: 123, 2: 345 };
console.log(foo[0]); // ok(读取)
foo[0] = 456; // Error: 属性只读

```

原生 JavaScript 数组，可以使用 TypeScript 提供的 ReadonlyArray<T> 接口
```ts
let foo: ReadonlyArray<number> = [1, 2, 3];
console.log(foo[0]); // ok
foo.push(4); // Error: ReadonlyArray 上不存在 `push`，因为他会改变数组
foo = foo.concat(4); // ok, 创建了一个复制

```


编译器能把一些特定的属性推断为有一个只含有 getter 但是没有 setter readonly ，例如在一个的属性，他能被推断为只读

###### 与 const 的不同
const
  用于变量;
  变量不能重新赋值给其他任何事物。
readonly
  用于属性;
  用于别名，可以修改属性;


### 泛型
设计泛型的关键目的是在成员之间提供有意义的约束，这些成员可以是:
  类的实例成员
  类的方法
  函数参数
  函数返回值

传统的例子是

```ts
class Queue {
  private data = [];
  push = item => this.data.push(item);
  pop = () => this.data.shift;
}

const queue = new Queue();
queue.push(0);
queue.push('1'); // Oops，一个错误
// 一个使用者，走入了误区 
console.log(queue.pop().toPrecision(1)); console.log(queue.pop().toPrecision(1)); 
```

解决办法为：push = (item: number) => this.data.push(item);

```ts
class Queue<T> {
  private data = [];
  push = (item: T) => this.data.push(item);
  pop = (): T => this.data.shift();
}
// 简单的使用
const queue = new Queue<number>();
queue.push(0);
queue.push("1"); // error

```

泛型常用 T 、 U 、 V 表示， TKey 和 TValue



### 类型推断

TypeScript 能根据一些简单的规则推断（检查）变量的类型

```ts
let foo = 123; // foo 是 'number'
let bar = 'hello'; // bar 是 'string'

foo = bar; // Error: 不能将 'string' 赋值给 `number`


function add(a: number, b: number) {
  return a + b;
}

bar = add(1, 2);
```

##### 赋值
函数参数类型/返回值也能通过赋值来推断

```ts
type Adder = (a: number, b: number) => number;
let foo: Adder = (a, b) => a + b;
```

##### 结构化 + 解构
这些简单的规则也适用于结构化的存在

### 类型兼容性
```ts
interface Point2D {
  x: number;
  y: number;
}

interface Point3D {
  x: number;
  y: number;
  z: number;
}

const point2D: Point2D = { x: 0, y: 10 };
const point3D: Point3D = { x: 0, y: 10, z: 20 };
function iTakePoint2D(point: Point2D) {
  /* do something */
}

iTakePoint2D(point2D); // ok, 完全匹配
iTakePoint2D(point3D); // 额外的信息，没关系
iTakePoint2D({ x: 0 }); // Error: 没有 'y'
```

### 变体

### 函数
##### 返回值

```ts
interface Point2D {
  x: number;
  y: number;
}
interface Point3D {
  x: number;
  y: number;
  z: number;
}

let iMakePoint2D = (): Point2D => ({ x: 0, y: 0 });
let iMakePoint3D = (): Point3D => ({ x: 0, y: 0, z: 0 });

iMakePoint2D = iMakePoint3D;
iMakePoint3D = iMakePoint2D; // ERROR: Point2D 不能赋值给 Point3D
```

##### 可选的和 rest 参数

```ts
let foo = (x: number, y: number) => {};
let bar = (x?: number, y?: number) => {};
let bas = (...args: number[]) => {};

foo = bar = bas;
bas = bar = foo;
```
##### 函数参数类型

##### 枚举
枚举与数字类型相互兼容
```ts
// 枚举与数字类型相互兼容
enum Status {
  Ready,
  Waiting,
}

let status1 = Status.Ready;
let num = 0;

status1 = num;
num = status;

```
来自于不同枚举的枚举变量，被认为是不兼容的：
```ts
enum Status {
  Ready,
  Waiting,
}
enum Color {
  Red,
  Blue,
  Green,
}

let status1 = Status.Ready;
let color = Color.Red;

status1 = color; // Error

```


##### 类

仅仅只有实例成员和方法会相比较，构造函数和静态成员不会被检查。
私有的和受保护的成员必须来自于相同的类。

##### 泛型
TypeScript 类型系统基于变量的结构，仅当类型参数在被一个成员使用时，才会影响兼容性。如下例子中，T 对兼容性没有影响：
```ts
interface Empty<T> {
  data: T;
}

let x: Empty<number>;
let y: Empty<string>;

x = y; // Error
```

如果尚未实例化泛型参数，则在检查兼容性之前将其替换为 any


### Never
never 类型是 TypeScript 中的底层类型。

1. 一个从来不会有返回值的函数
2. 一个总是会抛出错误的函数

##### 与 void 的差异
一旦有人告诉你，never 表示一个从来不会优雅的返回的函数时，你可能马上就会想到与此类似的 void，然而实际上，void 表示没有任何类型，never 表示永远不存在的值的类型。

当一个函数没有返回值时，它返回了一个 void 类型，但是，当一个函数根本就没有返回值时（或者总是抛出错误），它返回了一个 never，void 指可以被赋值的类型（在 strictNullChecking 为 false 时），其他任何类型不能赋值给 never，除了 never 本身以外

### 辨析联合类型
如果你使用类型保护风格的检查（==、===、!=、!==）或者使用具有判断性的属性（在这里是 kind），TypeScript 将会认为你会使用的对象类型一定是拥有特殊字面量的，并且它会为你自动把类型范围变小。
```ts
function area(s: Shape) {
  if (s.kind === 'square') {
    return s.size * s.size;
  } else if (s.kind === 'rectangle') {
    return s.width * s.height;
  } else if (s.kind === 'circle') {
    return Math.PI * s.radius ** 2;
  } else {
    // ok
    const _exhaustiveCheck: never = s;
  }
}
```
##### Switch
```ts
function area(s: Shape) {
  switch (s.kind) {
    case 'square':
      return s.size * s.size;
    case 'rectangle':
      return s.width * s.height;
    case 'circle':
      return Math.PI * s.radius ** 2;
    default:
      const _exhaustiveCheck: never = s;
  }
}
```


### 索引签名 
JavaScript 在一个对象类型的索引签名上会隐式调用 toString 方法，而在 TypeScript 中，为防止初学者砸伤自己的脚（我总是看到 stackoverflow 上有很多 JavaScript 使用者都会这样。），它将会抛出一个错误。

### 声明一个索引签名

```ts
// [index: string]: { message: string } 也可以是 { username: string}: { message: string }

const foo: {
  [index: string]: { message: string };
} = {};

// 储存的东西必须符合结构
// ok
foo['a'] = { message: 'some message' };

// Error, 必须包含 `message`
foo['a'] = { messages: 'some message' };

// 读取时，也会有类型检查
// ok
foo['a'].message;

// Error: messages 不存在
foo['a'].messages;
```



```ts
// ok
interface Foo {
  key: number;
  x: number;
  y: number;
}

// Error
interface Bar {
  [key: string]: number;
  x: number;
  y: string; // Error: y 属性必须为 number 类型
}
```

```ts
type Index = "a" | "b" | "c";
type FromIndex = { [k in Index]?: number };

const good: FromIndex = { b: 1, c: 2 };

// Error:
// `{ b: 1, c: 2, d: 3 }` 不能分配给 'FromIndex'
// 对象字面量只能指定已知类型，'d' 不存在 'FromIndex' 类型上
const bad: FromIndex = { b: 1, c: 2, a: 3 };

```



### 流动的类型

##### 复制类型和值

```ts
namespace importing {
  export class Foo {}
}

import Bar = importing.Foo;
let bar: Bar; // ok
```


##### 捕获变量的类型
也可以提供过typeof

##### 捕获类成员的类型

```ts
class Foo {
  foo: number; // 我们想要捕获的类型
}

declare let _foo: Foo;

// 与之前做法相同
let bar: typeof _foo.foo;
```


##### 捕获字符串的类型与值
```ts
const foo = 'Hello World';

// 使用一个捕获的类型
let bar: typeof foo;

// bar 仅能被赋值 'Hello World'
bar = 'Hello World'; // ok
bar = 'anything else'; // Error
```



```ts
const colors = {
    red: 'red',
    blue: 'blue'
  };
  
  type Colors = keyof typeof colors;
  
  let color: Colors; // color 的类型是 'red' | 'blue'
  color = 'red'; // ok
  color = 'blue'; // ok
//   color = 'anythingElse'; // Error
```



### 异常处理

除非你想用以非常通用（try/catch）的方式处理错误，否则不要抛出错误。

```ts
try {
  throw new Error('Something bad happened');
} catch (e) {
  console.log(e);
}
```

##### 子类型
RangeError  ReferenceError  SyntaxError TypeError URIError

##### 不清楚从哪里抛出错误

不清楚从哪里抛出错误 减少 跑出异常的语句

### 混合

https://segmentfault.com/a/1190000014121475

##### 交叉类型

##### 联合类型

##### 混合类型



### this

```ts
// Compile with --noImplicitThis

type Point = {
  x: number;
  y: number;
  moveBy(dx: number, dy: number): void;
};

let p: Point = {
  x: 10,
  y: 20,
  moveBy(dx, dy) {
    this.x += dx; // this has type Point
    this.y += dy; // this has type Point
  }
};

let foo = {
  x: 'hello',
  f(n: number) {
    this; // { x: string, f(n: number): void }
  }
};

let bar = {
  x: 'hello',
  f(this: { message: string }) {
    this; // { message: string }
  }
};
```